#ifndef B2D_H
#define B2D_H

#include <string>
#include "Box2D.h"
#include "b2Render.h"
#include "b2UserDataManager.h"
#include "b2ObjectFactory.h"

#include "max.cpp.h"


#include "Biped.h"
//#include "b2d_common.h"

using namespace std;

class b2d;


enum ContactState
{
	e_contactAdded,
	e_contactPersisted,
	e_contactRemoved,
};

struct ContactPoint
{
	b2Shape* shape1;
	b2Shape* shape2;
	b2Vec2 normal;
	b2Vec2 position;
	b2Vec2 velocity;
	b2ContactID id;
	ContactState state;
};

/*
 The external's class
 */
 
class DestructionListener : public b2DestructionListener
{
public:
	void SayGoodbye(b2Shape* shape);
	void SayGoodbye(b2Joint* joint);
		
	b2d* b2d_main;

};

		
 // for A_NOTHING methods (e.g. bang):
#define METHOD_NOTHING(CLASS, METHOD) (method)CLASS::MaxMethod<&CLASS::METHOD>::call
#define METHOD_GIMMIE(CLASS, METHOD) (method)CLASS::MaxMethodGimme<&CLASS::METHOD>::call

#define DEFAULT_FLOAT_ATTRIBUTE(ATTR_NAME,DEFAULT_VALUE) class_addattr((t_class*)maxClass,	\
									attr_offset_new(#ATTR_NAME,					\
													_sym_float32,					\
													0,							\
													(method)0L,					\
													(method)0L,					\
													calcoffset_cpp(me_dummy,ATTR_NAME))); \
									ATTR_NAME = DEFAULT_VALUE;

#define DEFAULT_LONG_ATTRIBUTE(ATTR_NAME,DEFAULT_VALUE) class_addattr((t_class*)maxClass,	\
									attr_offset_new(#ATTR_NAME,					\
													_sym_long,					\
													0,							\
													(method)0L,					\
													(method)0L,					\
													calcoffset_cpp(me_dummy,ATTR_NAME))); \
									ATTR_NAME = DEFAULT_VALUE;
									


/*
struct cmp_str 
{
   bool operator()(char const *a, char const *b) 
   {
      return std::strcmp(a, b) < 0;
   }
};
*/



class BoundaryListener : public b2BoundaryListener	
{
public:
	void Violation(b2Body* body);

	b2d* b2d_main;
};

const int32 k_maxContactPoints = 2048;

class ContactListener : public b2ContactListener
{
public:
	void Add(const b2ContactPoint* point);
	void Persist(const b2ContactPoint* point);
	void Remove(const b2ContactPoint* point);

	b2d* b2d_main;
};


			
class b2d : public MaxCpp<b2d>
{
public:
	
//	b2d();
	b2d(t_symbol * s = 0, long ac = 0, t_atom * av = 0);
	~b2d();// does max really manage the memory for stuff like this?
	
	/* methods */
	void bang();
	void draw(t_symbol * s, long ac, t_atom * av); // the draw comand (can take lots of args)
		
	void mouse(t_symbol * s, long ac, t_atom * av); // mouse
	
	void drawShapes(b2Body* body);
	void drawJoints(b2Joint* joint);
	
	void step();		// step the simulation
	
	void initworld();	// init world, will also reset
	void makestage();
		
	/* b2World attributes */
	long				maxbodies; // the maximum number of bodies allowable	
	
	long				dosleep;
	float				hz;
	long				iterations;

	void setWorldAABB(t_symbol * s, long ac, t_atom * av);
	float				worldAABB[4];	// the world AABB bounding box
	long				worldAABBcnt;

	void setGravity(t_symbol * s, long ac, t_atom * av);
	float				gravity[2];
	long				gravitycnt;
	



	/* rendering attributes */	
	long				debugDrawFlags[12];
	long				debugDrawFlagsCnt;
	
	long				solverParams[3];
	long				solverParamsCnt;

	long				outputuserdata;


	b2World* world; // a pointer of type b2World 
	b2Body* m_stage; // a bounding stage

	Biped* m_biped;


	b2MouseJoint* m_mouseJoint;


	void b2d::mousedown(const b2Vec2& p);
	void b2d::mousemovement(const b2Vec2& p);
	void b2d::mouseup();


	/* draw settings */
	


	/* getting and setting bodies */
	void get(t_symbol * s, long ac, t_atom * av);
	void set(t_symbol * s, long ac, t_atom * av);
	
	void grab(t_symbol * s, long ac, t_atom * av);
	
	
	void group(t_symbol * s, long ac, t_atom * av);
	
	void reset();
	
	/* this method catches all messages unassigned as attributes or methods */
	void anything(t_symbol * s, long ac, t_atom * av);
	void assist(void *box, long msg, long arg, char *dstString);

	/* inlets */
	void *m_proxy[1];/* 2 inlets requires 1 proxies */ 
	long m_inletNumber; /* where proxy will put inlet 

	
	/* outlets */
	t_object *			main_outlet;


	DebugDraw							debugDraw;

	b2UserDataManager*					userDataManager;
	b2ObjectFactory*					objectFactory;

	/* b2DestructionListener */
	void ShapeDestroyed(b2Shape* shape);
	void JointDestroyed(b2Joint* joint);

	t_atom outValue[2048];// allocate a bunch
	long outCount;// = 0;

	char nameBuffer[128];



protected:

	void initMaxMSPAttributes(t_symbol *s, long ac, t_atom *av);
	
	friend class DestructionListener;
//	friend class BoundaryListener;
//	friend class ContactListener;

//	ContactPoint m_points[k_maxContactPoints];
	int pointCount;
	DestructionListener m_destructionListener;
	//BoundaryListener m_boundaryListener;
//	ContactListener m_contactListener;
	
	bool b2mouseDown;
		
	
};

#endif	

